home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
SGI Developer Toolbox 6.1
/
SGI Developer Toolbox 6.1 - Disc 4.iso
/
public
/
bit
/
src
/
gif.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-08-01
|
25KB
|
1,033 lines
/*
* $Id: gif.c,v 0.91 1994/02/20 00:52:50 zhao Pre-Release $
*
*. This file is part of BIT shareware package. After the two weeks of
* free evaluation period, you are encouraged (required) to register
* your copy for a small registration fee, which is $35 for personal use
* and $50 for commercial, government and institutional use.
*
* Copyright(c) 1993, 1994 by T.C. Zhao.
* All rights reserved.
*
* Permission to use, copy, and distribute this software in its entirety
* for non-commercial purposes is hereby granted, provided that the
* above shareware and copyright notices and this permission notice
* appear in all copies and their documentation.
*
* This software may be modified for your own use, but modified versions
* may not be distributed without prior consent of the author.
*
* This software is provided "as is" without expressed or implied
* warranty of any kind.
*
*.
*
* CompuServ GIF related routines. Basically reads raster one row a time and
* then hand the pixels to the driver via function outputline in proper
* order.
*
* BUGS:
* Multi images are not handled. Graphics control not handled(except
* transparency)
*
*
* "The Graphics Interchange Format(c) is the Copyright property of
* CompuServ Incorporated. GIF(sm) is a Service Mark property of
* CompuServ Incorporated.
*
*/
#ifndef NO_GIF
#if !defined(lint) && defined(F_ID)
char *id_gif = "$Id: gif.c,v 0.91 1994/02/20 00:52:50 zhao Pre-Release $";
#endif
#include "bit.h"
#include "dmalloc.h"
static long gif_rlines; /* No. of lines read so far */
/********************************************************************
* Given GIF sequence no. i, starting from 0, figure out the image
* sequence number. If interlaced, the sequence will be independent
* of i, sort of a bug, but for this application, it is perfectly ok.
*********************************************************************/
static int
next_lineno(int i, int h, int interlace)
{
/* interlace delta */
static const int steps[5] =
{
8, 8, 4, 2, 0
};
/* starting scanline */
static const int start[5] =
{
0, 4, 2, 1, 0
};
static int pass, sofar, current;
int line;
/* init for each image */
if (i == 0)
pass = sofar = current = 0;
if (!interlace)
line = i;
else
{
line = current;
if ((current += steps[pass]) >= h)
current = start[++pass];
}
sofar++;
REPORT(sofar, gif_rlines);
/* check */
if (line >= h)
{
Bark("GIFNextLine", "Bad scan line");
return h - 1;
}
return line;
}
static const unsigned int gif_codemask[] =
{
0, 1, 3, 7, (1 << 4) - 1, (1 << 5) - 1, (1 << 6) - 1,
(1 << 7) - 1, (1 << 8) - 1, (1 << 9) - 1, (1 << 10) - 1,
(1 << 11) - 1, (1 << 12) - 1
};
/*****************************************************************
* Decoding routines
*
* The way the master driver is setup, it is difficult to handle
* multiple images stored in a single GIF file (which is rare).
*****************************************************************/
/******************* local functions ******************/
static int readextension(FILE *);
static void read_map(CMPTR, FILE *);
static int skip_extension(FILE * fp);
static int process_lzw_code(int);
static void outputline(unsigned char *);
/****************** decoder variables ******************/
/* this is the stuff handled by the decoder */
static int transparency;
/* copies of the global variable */
static IPTR gif_lm;
static char *gif_ifile;
static long gif_total; /* no. of pixels read so far */
/****************************************************************
* ouptut line simply copies one scanline at a time to the master
* image structure, taking into account the possibility of
* interlacing
****************************************************************/
static void
outputline(register unsigned char *line)
{
register ci_t **pp = gif_lm->mraster, *po;
register unsigned char *pi = line;
static int lines;
int k;
if (gif_total == 0) /* first entry */
lines = 0;
k = next_lineno(lines, gif_lm->h, gif_lm->interlace);
gif_total += gif_lm->w;
for (po = pp[k], line += gif_lm->w; pi < line;)
*po++ = *pi++;
lines++;
}
#define IMAGESEP 0x2C /* ',' */
static int
read_descriptor_block(IPTR im, int global_gifmap)
{
int local_gifmap;
int inbyte;
unsigned char buf[15];
const char *f = "GIF_desc";
FILE *fp = im->fp;
/*
* read the extension if any, and do nothing about it until get the image
* separator
*/
if (skip_extension(fp) != IMAGESEP)
{
Bark(f, "%s: No separator or BadBlockMarker", gif_ifile);
return -1;
}
/* offset has no meaning, skip it, gif_total 2 + 2= 4bytes */
if (Badfread(buf, 1, 4, fp))
return -1;
/* True image size */
im->w = get2LSBF(fp);
im->h = get2LSBF(fp);
inbyte = getc(fp);
im->interlace = (inbyte & 0x40) != 0;
local_gifmap = (inbyte & 0x80);
set_iformat_info(im, im->interlace ? "Interlaced" : "");
if (!global_gifmap && !local_gifmap)
{
Bark(f, "%s: No ColorMap", gif_ifile);
/* return -1; *//* might want to continue */
}
/* if local map, replace the global map */
if (local_gifmap)
{
local_gifmap = (inbyte & 0x07) + 1;
im->cmap->colors = (1 << local_gifmap);
read_map(im->cmap, fp);
}
return 0;
}
/**********************************************************************
* Get GIF image descriptions
**********************************************************************/
int
GIF_desc(IPTR im)
{
int global_gifmap;
unsigned char buf[15];
FILE *fp = im->fp;
gif_lm = im;
gif_ifile = im->ifile;
transparency = 0;
/* img_open should have checked signature Skip it. total 6 bytes */
if (Badfread(buf, 1, 6, fp))
return -1;
/* now the packed field */
if (Badfread(buf, 1, 7, fp))
return -1;
/*
* primary color resolution doesn't mean too much here ignore it. Also
* bit 4 under 89a could be 1, so ignore that too.
*/
im->lsx = buf[0] + (buf[1] << 8);
im->lsy = buf[2] + (buf[3] << 8);
global_gifmap = (buf[4] & 0x80);
im->colors = im->cmap->colors = 1 << (1 + (buf[4] & 0x07));
im->cmap->ucolors = im->colors;
/* im->bkcolor= buf[5]; */
im->aspect = buf[6] ? (1000.0 * (buf[6] + 15) / 64.0) : 1000;
/* err= buf[4] & 0x08; */
if (global_gifmap)
read_map(im->cmap, fp);
/*
* supposedly, from here to EOS (';'), the data stream may be repeated
* more than one times, currently, the program does not handle this,
* maybe a next field in IPTR ?????
*/
return read_descriptor_block(im, global_gifmap);
}
/* Get a single data block. */
static int
getblock(FILE * fp, char *buf)
{
int count;
if ((count = getc(fp)) != EOF && count != 0)
count = fread(buf, 1, count, fp);
return count;
}
#define EXTENSION 0x21 /* '!' introducer */
#define GIFEXT_PT 0x01 /* plain text */
#define GIFEXT_APP 0xFF /* application */
#define GIFEXT_GC 0xF9 /* graphics control */
#define GIFEXT_COM 0xFE /* comment */
#define Bad_ext(str) Bark(f,"%s: Bad %s extension",gif_ifile,str)
#define Msg_ext(str) M_info(gif_ifile,str)
/*
* As long as we are not doing the extension, print it out to stderr
*/
static int
readextension(FILE * fp)
{
int count = 0, label;
char buf[258];
const char *f = "GIF_ext";
int ll, tt, cw, ch, bc, tc;
float fsize;
label = getc(fp);
switch (label)
{
case GIFEXT_PT: /* plain text extension */
Msg_ext("PlainText extension");
if (getc(fp) != 12)
{
Bad_ext("PlainText");
return EOF;
}
ll = get2LSBF(fp); /* left */
tt = get2LSBF(fp); /* top */
get2LSBF(fp); /* total width */
get2LSBF(fp); /* total height */
cw = getc(fp); /* cell width */
ch = getc(fp); /* cell height */
tc = getc(fp); /* text color */
bc = getc(fp); /* bk color */
while ((count = getblock(fp, buf)) != 0 && count != EOF)
{
rgba_t ttc, bbc;
buf[count] = '\0';
M_warn(gif_ifile, buf);
fsize = Min(cw, ch);
if (fsize < 6.0)
fsize = 6.0;
ttc = Pack(gif_lm->cmap->ct[0][tc], gif_lm->cmap->ct[1][tc],
gif_lm->cmap->ct[2][tc]);
bbc = Pack(gif_lm->cmap->ct[0][bc], gif_lm->cmap->ct[1][bc],
gif_lm->cmap->ct[2][bc]);
img_add_text(gif_lm, buf, "", fsize, 0.0, ttc,
ll, (tt - fsize), !transparency, bbc, 0, 0, 0, 0);
}
break;
case GIFEXT_COM: /* a comment extension */
Msg_ext("CommentsExtension:");
while ((count = getblock(fp, buf)) != 0 && count != EOF)
{
buf[count] = '\0';
Msg_ext(buf);
}
break;
case GIFEXT_GC: /* graphics control */
Msg_ext("GraphicsControl extension");
while ((count = getblock(fp, buf)) != 0 && count != EOF)
{
transparency = (buf[0] & 1); /* packed bytes */
}
break;
case GIFEXT_APP: /* application extension */
Msg_ext("ApplicationExtension");
if (getc(fp) != 11) /* block length */
M_warn("GifExt", "wrong block length");
fread(buf, 1, 8, fp);
buf[8] = '\0';
Msg_ext(buf);
fread(buf, 1, 3, fp);
while ((count = getblock(fp, buf)) != 0 && count != EOF)
{
buf[count] = '\0';
Msg_ext(buf);
}
break;
default:
Bark(f, "%s: Bogus extension byte 0x%02x", gif_ifile, label);
break;
}
return count;
}
static int
skip_extension(FILE * fp)
{
int pchar, err = 0;
while (!err && (pchar = getc(fp)) != EOF && pchar != IMAGESEP)
{
switch (pchar)
{
case '\0':
break;
case EXTENSION:
err = readextension(fp);
break;
default:
M_warn("GIFextension", "%s: Bogus byte 0x%02x",
gif_ifile, pchar);
return EOF;
}
}
return err ? EOF : pchar;
}
static void
read_map(CMPTR m, FILE * fp)
{
register pc_t *r = m->ct[0], *g = m->ct[1], *b = m->ct[2], *rs;
for (rs = r + m->colors; r < rs; r++, g++, b++)
{
*r = getc(fp);
*g = getc(fp);
*b = getc(fp);
}
}
#define LZW_INIT 9000
static int bpp, ClearCode, EOFCode, CodeSize;
static const char *lfn = "GIF_load";
#include <ctype.h>
static unsigned char *lhead, *lbuf, *stackp; /* local buffers */
int
GIF_load(IPTR im)
{
register int bits = 0, err, count, code = -1;
register unsigned datum = 0;
register unsigned char *ch;
unsigned char buf[257];
FILE *fp = im->fp;
gif_rlines = progress_report("Loading GIF...", im->h);
CodeSize = getc(fp);
if (CodeSize > 8 || CodeSize < 2)
{
Bark(lfn, "%s: Bad CodeSize: %d", gif_ifile, CodeSize);
return -1;
}
/* initialize the decompressor */
err = gif_total = 0;
(void) process_lzw_code(LZW_INIT);
while (!err && (count = getc(fp)) != EOF && count > 0)
{
err = Badfread(buf, 1, count, fp);
for (ch = buf; !err && count-- > 0; ch++)
{
datum += *ch << bits;
bits += 8;
while (bits >= CodeSize && !err)
{
code = datum & gif_codemask[CodeSize];
datum >>= CodeSize;
bits -= CodeSize;
err = (code == EOFCode) || process_lzw_code(code) ||
(gif_total > (long) im->w * im->h);
}
}
if (err && code == EOFCode)
err = 0;
if (code != EOFCode && gif_total > (long) im->w * im->h)
{
Bark(lfn, "%s: Raster full before EOI", gif_ifile);
err = 1;
}
}
if (!err)
{
if ((code = getc(fp)) == EXTENSION)
{
ungetc(code, fp);
while ((code = skip_extension(fp)) != EOF && code != IMAGESEP)
;
}
if (code == IMAGESEP)
{
Bark(lfn, "%s: More than one image present", gif_ifile);
}
else if (code != EOF &&
((fread(buf, 1, 50, fp), getc(fp))) != EOF)
{
M_info(lfn, "%s: Garbage(> 50bytes) at end", gif_ifile);
ch = buf;
while (ch - buf < 50)
{
fputc(isprint(*ch) ? *ch : ' ', fp);
ch++;
}
}
}
remove_progress_report();
im->t2b = 1;
count = gif_total / im->w;
/* final check: get pixels that are decoded but yet to be output */
if (count < im->h)
{
int leftover;
leftover = lbuf - lhead;
M_warn(lfn, "total %ld should be %d", (gif_total + leftover),
im->w * im->h);
if (leftover)
outputline(lhead);
}
/*
* if more than 1/5 image is read, return positive value so that driver
* will try to display it.
*/
return (count >= (im->h / 5)) ? count : -1;
}
/*********************************************************************
* process_lzw_code - Process a compression code. "clear" resets the
* code table. Otherwise make a new code table entry, and output the
* bytes associated with the code.
*
* Based on gifpaste by Kipp Hickman @ Silicon Graphics
*/
#define OUTPIX(c) *lbuf++ = (c)
#define MC_SIZE 4097
static int
process_lzw_code(register int code)
{
register int incode, i;
static unsigned char firstchar;
static unsigned char stack[MC_SIZE];
static int avail, oldcode;
static unsigned char suffix[MC_SIZE];
static unsigned short prefix[MC_SIZE];
if (code == LZW_INIT)
{
if (!lhead)
lbuf = lhead = malloc((gif_lm->w + 1 + 4096));
else
lbuf = lhead = realloc(lhead, (gif_lm->w + 1 + 4096));
bpp = CodeSize;
ClearCode = 1 << bpp;
EOFCode = ClearCode + 1;
CodeSize = bpp + 1;
for (incode = ClearCode; --incode >= 0;)
{
*(suffix + incode) = incode;
*(prefix + incode) = 0;
}
avail = ClearCode + 2;
oldcode = -1;
stackp = stack;
return lbuf ? 0 : -1;
}
if (code == ClearCode)
{
CodeSize = bpp + 1;
avail = ClearCode + 2;
oldcode = -1;
return 0;
}
/* this is possible only if the image file is corrupt */
if (code > avail || code < 0)
{
Bark(lfn, "%s: Bad code 0x%04.4x", gif_ifile, code);
return -1;
}
if (oldcode == -1)
{
OUTPIX(suffix[code]);
firstchar = oldcode = code;
return 0;
}
incode = code;
if (code == avail)
{ /* the first code is always < avail */
*stackp++ = firstchar;
code = oldcode;
}
while (code > ClearCode)
{
*stackp++ = suffix[code];
code = prefix[code];
}
if (avail >= 4096)
{
Bark(lfn, "%s: BadBlock -- Table full", gif_ifile);
return -1;
}
*stackp++ = firstchar = suffix[code];
prefix[avail] = oldcode;
suffix[avail] = firstchar;
avail++;
if (((avail & gif_codemask[CodeSize]) == 0) && (avail < 4096))
{
CodeSize++;
}
oldcode = incode;
do
{
OUTPIX(*--stackp);
}
while (stackp > stack);
/* if we've got more than one scanline, output */
incode = lbuf - lhead;
if (incode >= gif_lm->w)
{
lbuf = lhead;
while (incode >= gif_lm->w)
{
outputline(lbuf);
incode -= gif_lm->w;
lbuf += gif_lm->w;
}
/* copy the left over */
for (i = 0; i < incode; i++)
lhead[i] = *lbuf++;
lbuf = lhead + incode;
}
return 0;
}
/********************************************************************
* GIF encoding routine.
* This one I started from scratch and the only reference is the
* specification.
********************************************************************/
static int q_color = 256;
static int gifdither, method, interlace;
static int gifdump(FILE *, IPTR); /* real encoder */
/* Driver calls this routine to write gif files */
int
GIF_dump(IPTR im)
{
FILE *fp = im->fp;
if (!IS_CI(im))
{ /* should be ok, check anyway */
set_quant_parameters(q_color, gifdither, method);
img_convert_type(im, im->io->type);
}
return gifdump(fp, im);
}
/************************************************************
* options for the driver: no. of colors and interlace
*************************************************************/
extern const char *qstring[];
/* string version of the option */
const char *
GIF_wdefault(const IPTR ip)
{
static char dinfo[150];
if (IS_CPACK(ip))
{
sprintf(dinfo, "%s to %d colors. %s", qstring[method],
q_color, interlace ? "Interlace" : "");
}
else
{
sprintf(dinfo, "%d colors %s ", ip->cmap->colors,
(interlace = ip->interlace) ? "Interlace" : "");
}
return dinfo;
}
/* For colormapped image, no further quantization is allowed */
int
GIFdump_init(IPTR img)
{
if (!IS_CI(img))
{
set_quant_max_color(256);
get_quant_p("GIF parameters", &method, &gifdither, &q_color,
"Interlace", &interlace, 0, 0);
set_quant_parameters(q_color, gifdither, method);
}
else
interlace = !interlace;
img->interlace = interlace;
return 0;
}
/************************************************************
*
* Write image to a disk file in GIF format.
*
************************************************************/
/*
* current char, cchar, must be of signed type, and code and prefix must be
* at least 12 bits long.
*/
typedef struct strspace_
{
struct strspace_ *next; /* link */
int code; /* emit code */
int cchar; /* current char */
}
Strtab;
typedef struct
{
int prefix, cchar, code;
}
WorkStr;
#define MAXTABL 4097
static Strtab *strtab[MAXTABL], strspace[MAXTABL];
/**************************************************************
* Check if current string is already in the string table
**************************************************************/
static int
in_table(register WorkStr * cstr)
{
register Strtab *p = strtab[cstr->prefix];
#if 1
for (; p && (p->cchar != cstr->cchar); p = p->next)
;
return p ? p->code : -1;
#else
for (; p; p = p->next)
if (p->cchar == cstr->cchar)
return p->code;
return -1;
#endif
}
#define USE_TAB_FUNC
/*
* A macro and function are supplied to insert a string into the
* string table.
*/
#ifdef USE_TAB_FUNC
static void
addto_table(register WorkStr * cstr, register int code)
{
register Strtab *p = &strspace[code];
p->code = code;
p->cchar = cstr->cchar;
p->next = strtab[cstr->prefix];
strtab[cstr->prefix] = p;
}
#else
#define addto_table(cstr, ccode) \
do { \
register Strtab *p = &strspace[ccode]; \
p->code = ccode; \
p->cchar = cstr->cchar; \
p->next = strtab[cstr->prefix]; \
strtab[cstr->prefix] = p; \
} while(ZERO)
#endif
static void output_lzw_code(unsigned code);
static void init_table(int);
static ci_t *get_scan_line(IPTR, int);
static FILE *fp;
/*******************************************************************
* Write colormaps. Also takes care the cases where the entries
* in colormap is not 2^n, need to pad it to 2^n, otherwise, the
* encoder chokes.
*******************************************************************/
static void
write_map(CMPTR m, FILE * ffp)
{
register pc_t *r = m->ct[0], *g = m->ct[1], *b = m->ct[2], *rs;
register int mc = m->colors;
for (rs = r + mc; r < rs; r++, g++, b++)
{
putc((int) *r, ffp);
putc((int) *g, ffp);
putc((int) *b, ffp);
}
/* pad with the first entry */
for (; mc < (1 << bpp); mc++)
{
putc(m->ct[0][0], ffp);
putc(m->ct[1][0], ffp);
putc(m->ct[2][0], ffp);
}
#ifdef MTRACE
M_trace("GifWrtMap", "Appears ok - exiting");
#endif
}
/*
* write the image description
*/
static int
write_desc(IPTR im, FILE * ffp)
{
int packed;
unsigned char buf[10];
/* get bits per pixel first */
bpp = 0;
while (im->cmap->colors > (1 << bpp))
bpp++;
if (bpp < 1 || bpp > 8)
{
Bark("GIF_dump", "%s: Bad bpp=%d", im->ofile, bpp);
bpp = 1;
}
if (Badfwrite("GIF87a", 1, 6, ffp))
{
Bark("GIF_dump", im->ofile);
return -1;
}
put2LSBF(im->lsx, ffp);
put2LSBF(im->lsx, ffp);
packed = 0x80 /* always output global map */
+ ((bpp - 1) << 4) /* cr. does not mean much */
+ (bpp - 1); /* bits_per_pixel-1 */
putc(packed, ffp);
putc(0, ffp);
putc(0, ffp); /* bk color and aspect ratio */
/* global color map. */
write_map(im->cmap, ffp);
/* image descriptions */
buf[0] = ','; /* image seperator */
buf[1] = buf[2] = buf[3] = buf[4] = 0; /* offsets */
if (Badfwrite(buf, 1, 5, ffp))
return -1;
/* raster dimensions */
put2LSBF(im->w, ffp);
put2LSBF(im->h, ffp);
/* local_gifmap, interlace, etc. only set interlace if requested */
putc(interlace ? 0x40 : 0, ffp);
#ifdef MTRACE
M_trace("GifWrtDesc", "Appears ok - exiting");
#endif
return 0;
}
/*******************************************************************
* The encoder
*******************************************************************/
static int
gifdump(FILE * ffp, IPTR im)
{
register int j, code, ccode;
register ci_t *scan, *ss;
register WorkStr *cstr;
register int colors;
WorkStr workstring;
fp = ffp;
gif_rlines = progress_report("Writing GIF ...", im->h);
if (write_desc(im, fp) < 0)
return -1;
/*
* IMPORTANT: number of colors handed to this routine might not be 2^n,
* need to make it so to fool the encoder (colors-1 need to be full bits)
*/
colors = 1 << bpp;
/* min bpp by defination is no smaller than 2 */
if (bpp < 2)
bpp = 2; /* initial codesize */
putc(bpp, fp);
ClearCode = 1 << bpp; /* set clear and end codes */
EOFCode = ClearCode + 1;
CodeSize = bpp + 1; /* start encoding */
init_table(colors); /* initialize the LZW tables */
cstr = &workstring;
ccode = EOFCode + 1;
cstr->prefix = -1;
/*
* start raster stream. Old way of doing things, that is as soon as we
* get 4095, a clearcode is emitted.
*/
for (j = 0; j < im->h; j++)
{
scan = get_scan_line(im, j);
for (ss = scan + im->w; scan < ss; scan++)
{
cstr->cchar = (*scan & (colors - 1));
if (cstr->prefix >= 0)
{
if ((code = in_table(cstr)) >= 0)
{
cstr->prefix = code;
}
else
{
addto_table(cstr, ccode);
output_lzw_code(cstr->prefix);
cstr->prefix = cstr->cchar;
if (ccode >= (1 << CodeSize))
CodeSize++;
ccode++;
if (ccode >= 4096)
{
output_lzw_code(cstr->prefix);
init_table(colors);
ccode = EOFCode + 1;
cstr->prefix = -1;
}
}
}
else
{ /* root entry */
cstr->prefix = cstr->code = cstr->cchar;
}
}
}
output_lzw_code(cstr->prefix);
output_lzw_code(EOFCode);
putc(0, fp); /* end block */
putc(';', fp); /* end stream */
remove_progress_report();
return fflush(fp);
}
static void
init_table(int rootlen)
{
register int i;
output_lzw_code(ClearCode);
CodeSize = bpp + 1;
for (i = 0; i < rootlen; i++)
{
strspace[i].next = 0;
strspace[i].code = i;
strspace[i].cchar = -1;
strtab[i] = &strspace[i];
}
for (; i < MAXTABL; i++)
strtab[i] = strspace[i].next = 0;
}
/**************************************************************
* Packing and output an LZW code(bpp+1 to 12 bits long).
*
* Note: accum must be at least 19 bits long. accum and bits must be
* re-initialized when EOFCode is written out.
*/
static void
output_lzw_code(register unsigned int code)
{
static unsigned int bytes, bits;
static unsigned char bbuf[255 + 3];
static unsigned long accum;
register unsigned char *ch;
accum &= gif_codemask[bits];
accum |= (code << bits);
bits += CodeSize;
ch = bbuf + bytes;
bytes += bits >> 3;
while (bits >= 8)
{
bits -= 8;
*ch++ = (accum & 255);
accum >>= 8;
}
if (bytes >= 254 || code == EOFCode)
{
if (code == EOFCode && bits)
{
*ch = (accum & 255);
bytes++;
bits = accum = 0;
}
putc(bytes, fp);
(void) fwrite(bbuf, 1, bytes, fp);
bytes = 0;
}
}
/*
* Hand one scan line a time to the compressor.
*/
static ci_t *
get_scan_line(IPTR im, int i)
{
int k = next_lineno(i, im->h, interlace);
return ((ci_t **) im->mraster)[im->h - 1 - k];
}
#endif /* !NO_GIF */